<?php

class SimpleImage
{

	/**
	 * 图片资源
	 * @var
	 */
	protected $image;

	/**
	 * 图片类型
	 * @var
	 */
	protected $image_type;

	/**
	 * 海报背景高度
	 * @var
	 */
	protected $backgroundHeight;

	/**
	 * 海报背景宽度
	 * @var
	 */
	protected $backgroundWidth;

	/**
	 * 字体颜色
	 * @var string
	 */
	protected $fontColor = "0,0,0";

	/**
	 * 字体大小
	 * @var string
	 */
	protected $fontSize = "26";

	/**
	 * 字体路径
	 */
	protected $fontPath = "";

	/**
	 * 图片资源是否可以保存
	 * @var bool
	 */
	protected $saveStatus = true;

	/**
	 * 图片类型
	 * @var
	 */
	protected $mime;

	/**
	 * 获取远程图片加速
	 * @param string $url
	 * @return array
	 */
	public function downloadImage(string $url)
	{

		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_ENCODING, ""); //加速 这个地方留空就可以了
		curl_setopt($ch, CURLOPT_HEADER, 0);
		$output    = curl_exec($ch);
		$http_code = curl_getinfo($ch);
		curl_close($ch);

		return [$output, $http_code['content_type'] ?? ''];
	}

	/**
	 * 处理图片
	 * @param string $url
	 * @return array
	 */
	private function checkImagePath(string $url, $expedite = false)
	{

		$preg = "/^http(s)?:\\/\\/.+/";
		if (preg_match($preg, $url) && $expedite) {

			[$image, $mime] = $this->downloadImage($url);
			$im     = imagecreatefromstring($image);
			$width  = imagesx($im);
			$height = imagesy($im);
		} else {

			$is_string = is_string($url);
			$info      = $is_string ? getimagesize($url) : getimagesizefromstring($url);
			$function  = $is_string ? 'imagecreatefrom' . image_type_to_extension($info[2], false) : 'imagecreatefromstring';
			$im        = $function($url);
			$width     = $info ? $info[0] : 0;
			$height    = $info ? $info[1] : 0;
			$mime      = $info ? $info['mime'] : '';
		}

		return [$im, $width, $height, $mime];
	}

	/**
	 * @param $width
	 * @param $height
	 * @param $resWidth
	 * @param $resHeight
	 * @return void
	 */
	public function coverImage($res, $width, $height, $resWidth, $resHeight)
	{

		/* 计算裁剪宽度和高度 */
		$judge    = (($resWidth / $resHeight) > ($width / $height));
		$resize_w = $judge ? ($resWidth * $height) / $resHeight : $width;
		$resize_h = !$judge ? ($resHeight * $width) / $resWidth : $height;
		$start_x  = $judge ? ($resize_w - $width) / 2 : 0;
		$start_y  = !$judge ? ($resize_h - $height) / 2 : 0;

		//	创建小画布
		$smallImage = imagecreatetruecolor($resize_w, $resize_h);

		//	创建透明背景色，主要127参数，其他可以0-255，因为任何颜色的透明都是透明
		$color = imagecolorallocatealpha($smallImage, 255, 255, 255, 127);

		//	指定颜色
		imagecolortransparent($smallImage, $color);

		//	保留透明颜色
		imagesavealpha($smallImage, true);

		//	填充图片颜色
		imagefill($smallImage, 0, 0, $color);

		//	将原图放进小画布
		imagecopyresampled($smallImage, $res, 0, 0, 0, 0, $resize_w, $resize_h, $resWidth, $resHeight);

		return [$smallImage, $start_x, $start_y];

	}

	/**
	 * 处理图像
	 * @param $res object 图片资源
	 * @param $width float 设置宽度
	 * @param $height float 设置高度
	 * @param $resWidth float 图片宽度
	 * @param $resHeight float 图片高度
	 * @param $mine string 图片类型
	 * @param $left float 距离左侧,默认=0
	 * @param $top float 距离顶部,默认=0
	 * @param $opacity float 透明度
	 * @param $model float 处理模式
	 * @return $this
	 */
	public function processImage($res, $width, $height, $resWidth, $resHeight, $mine, $left = 0, $top = 0, $opacity = 100, $model = "cover")
	{

		//	建立画板 ，缩放图片至指定尺寸
		$canvas = imagecreatetruecolor($width, $height);

		//	创建透明背景色，主要127参数，其他可以0-255，因为任何颜色的透明都是透明
		$color = imagecolorallocatealpha($canvas, 255, 255, 255, 127);

		//	指定颜色为透明（做了移除测试，发现没问题）
		imagecolortransparent($canvas, $color);

		//	保留透明颜色
		imagesavealpha($canvas, true);

		//	填充图片颜色
		imagefill($canvas, 0, 0, $color);

		//关键函数，
		//参数（目标资源，源，目标资源的开始坐标x,y, 源资源的开始坐标x,y,目标资源的宽高w,h,源资源的宽高w,h）
		if ($model == "cover") {
			[$smallImage, $start_x, $start_y] = $this->coverImage($res, $width, $height, $resWidth, $resHeight);
			imagecopyresampled($canvas, $smallImage, 0, 0, $start_x, $start_y, $width, $height, $width, $height);
		} else {
			imagecopyresampled($canvas, $res, 0, 0, 0, 0, $width, $height, $resWidth, $resHeight);
		}

		$left = $left < 0 ? $this->backgroundWidth - abs($left) - $width : $left;
		$top  = $top < 0 ? $this->backgroundHeight - abs($top) - $height : $top;

		if (strpos($mine, "png") !== false) {

			imagecopy($this->image, $canvas, $left, $top, 0, 0, $width, $height);

		} else {

			//	放置图像
			imagecopymerge($this->image, $canvas, $left, $top, 0, 0, $width, $height, $opacity);

		}

		imagedestroy($canvas);
		imagedestroy($res);

		return $this;

	}

	/**
	 * 设置背景图片
	 * @date (2022/4/11 16:49:55)
	 * @param $background (背景图片绝对路径)
	 * @return $this
	 * @author (WangDaBao)
	 */
	public function setBackground($background)
	{

		//  背景方法
		$backgroundInfo = getimagesize($background);

		//  设置保存路径
		$this->mime = $backgroundInfo["mime"];

		//
		$this->image_type = $backgroundInfo[2];

		//	获取方法名称
		$backgroundFun = 'imagecreatefrom' . image_type_to_extension($this->image_type, false);

		//	创建背景资源
		$background = $backgroundFun($background);

		//	背景宽度
		$this->backgroundWidth = imagesx($background);

		//	背景高度
		$this->backgroundHeight = imagesy($background);

		$this->image = imageCreatetruecolor($this->backgroundWidth, $this->backgroundHeight);

		$color = imagecolorallocate($this->image, 0, 0, 0);

		imagefill($this->image, 0, 0, $color);

		//	颜色透明
		imageColorTransparent($this->image, $color);

		//	将背景图合并到背景
		imagecopyresampled(
			$this->image,
			$background,
			0,
			0,
			0,
			0,
			imagesx($background),
			imagesy($background),
			imagesx($background),
			imagesy($background)
		);
		imagedestroy($background);

		return $this;

	}

	/**
	 * 填充文字
	 * @date (2022/4/11 16:39:22)
	 * @param $text (文字内容)
	 * @param $left (距离左侧位置)
	 * @param $top (距离顶部位置)
	 * @param $fontPath (字体路径)
	 * @param $fontSize (字体大小)
	 * @param $fontColor (字体颜色)
	 * @param $angle (倾斜角度)
	 * @author (WangDaBao)
	 */
	public function setText($text = "测试文字", $left = 0, $top = 0, $fontPath = null, $fontSize = null, $fontColor = null, $angle = null)
	{

		if (!$this->image) {
			throw new Exception("请先调用setBackground方法设置背景图片");
		}

		$fontPath = $fontPath ?: getcwd() . "/uploads/fonts/simsun.ttc";

		if (!file_exists($fontPath)) {
			throw new Exception("检测不到字体文件,请检查路径是否正确");
		}

		//  格式化颜色
		[$R, $G, $B] = explode(',', $fontColor ?: $this->fontColor);

		//  设置颜色
		$fontColor = imagecolorallocate($this->image, $R, $G, $B);

		//  校正左侧距离
		$left = $left < 0 ? $this->backgroundWidth - abs($left) : $left;

		//  校正顶部距离
		$top = $top < 0 ? $this->backgroundHeight - abs($top) : $top;

		imagettftext(
			$this->image,
			$fontSize ?: $this->fontSize,
			$angle ?: 0,
			$left,
			$top,
			$fontColor,
			$fontPath ?: $this->fontPath,
			$text);

		return $this;

	}

	/**
	 * 设置圆形图片
	 * @param $url string 图片地址,绝对路径
	 * @param $left float 距离左侧位置
	 * @param $top float 距离顶部位置
	 * @param $width float 图片宽度
	 * @param $height float 图片高度
	 * @param $opacity float 图片透明度,最大100,最小0
	 * @param $expedite bool 是否加速处理图片
	 * @param $model string 图片显示模式:cover=等比例缩放,other=不做任何处理
	 * @return void
	 */
	public function setFilletImage($url = "", $left = 0, $top = 0, $width = 100, $height = 100, $opacity = 100, $expedite = false, $model = "cover")
	{

		if (!$this->image) {
			throw new Exception("请先调用setBackground方法设置背景图片");
		}

		//	图片,宽度,高度
		[$res, $resWidth, $resHeight, $mine] = $this->checkImagePath($url, $expedite);

		// 居中等比例缩放
		[$smallImage] = $this->coverImage($res, $width, $height, $resWidth, $resHeight);

		//	建立画布
		$circular = imagecreatetruecolor($resWidth, $resHeight);

		//	设置支持透明度
		imagesavealpha($circular, true);

		//	拾取一个完全透明的颜色,最后一个参数127为全透明
		$bg = imagecolorallocatealpha($circular, 255, 255, 255, 127);

		//	填充
		imagefill($circular, 0, 0, $bg);

		//	取宽高中的最小值
		$wh = min($width, $height);

		//	得出半径
		$r = $wh / 2; //圆半径

		//	填充画布
		for ($x = 0; $x < $wh; $x++) {
			for ($y = 0; $y < $wh; $y++) {
				$rgbColor = imagecolorat($smallImage, $x, $y);
				if (((($x - $r) * ($x - $r) + ($y - $r) * ($y - $r)) < ($r * $r))) {
					imagesetpixel($circular, $x, $y, $rgbColor);
				}
			}
		}

		return $this->processImage($circular, $wh, $wh, $wh, $wh, $mine, $left, $top, $opacity, $model);

	}

	/**
	 * 填充图片
	 * @date (2022/4/11 17:15:49)
	 * @param string $url (图片路径或图片资源)
	 * @param int    $left (距离左侧像素)
	 * @param int    $top (距离顶部像素)
	 * @param int    $width (图片宽度)
	 * @param int    $height (图片高度)
	 * @param int    $opacity (透明度)
	 * @param bool   $expedite (此图片是否需要加速处理)
	 * @return $this
	 * @author (WangDaBao)
	 */
	public function setImage($url = "", $left = 0, $top = 0, $width = 100, $height = 100, $opacity = 100, $expedite = false, $model = "cover")
	{

		if (!$this->image) {
			throw new Exception("请先调用setBackground方法设置背景图片");
		}

		// 图片资源,图片宽度,图片高度,图片类型
		[$res, $resWidth, $resHeight, $mine] = $this->checkImagePath($url, $expedite);

		return $this->processImage($res, $width, $height, $resWidth, $resHeight, $mine, $left, $top, $opacity, $model);

	}

	/**
	 * 输出图片到网页
	 * @date (2022/4/11 17:18:22)
	 * @author (WangDaBao)
	 */
	public function show()
	{

		header("content-type: " . $this->mime);

		if (!$this->image) {
			throw new Exception("图片资源为空");
		}

		if (strpos($this->mime, "jpeg") !== false) {
			imagejpeg($this->image);
		}

		if (strpos($this->mime, "png") !== false) {
			imagepng($this->image);
		}

		if (strpos($this->mime, "gif") !== false) {
			imagegif($this->image);
		}

		//  释放图片资源
		imagedestroy($this->image);

		$this->saveStatus = false;

		return $this;
	}

	/**
	 * 保存图片
	 * @date (2022/4/11 17:32:49)
	 * @param      $filename (图片存放路径)
	 * @param int  $compression (图片质量)
	 * @param null $permissions
	 * @author (WangDaBao)
	 */
	public function save($filename, $compression = 75)
	{

		if (!$filename) {
			throw new Exception("保存路径不能为空");
		}

		if (!$this->saveStatus) {
			throw new  Exception("请关闭输出到浏览器,再保存");
		}

		if (strpos($this->mime, "jpeg") !== false) {
			imagejpeg($this->image, $filename, $compression);
		}

		if (strpos($this->mime, "png") !== false) {
			imagepng($this->image, $filename);
		}

		if (strpos($this->mime, "gif") !== false) {
			imagegif($this->image, $filename);
		}

		if (!file_exists($filename)) {
			throw new Exception("图片生成失败");
		}

		//  释放掉图片资源
		imagedestroy($this->image);

		return true;
	}

}
